﻿//////////////////////////////////////////////////////////////////////////////
//
// Copyright © 1998-2024 Glodon Company Limited.
//
// Licensed under the MIT License
//
// Permission is hereby granted, free of charge, to any person obtaining a
// copy of this software and associated documentation files (the “Software”),
// to deal in the Software without restriction, including without limitation
// the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
// sell copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
// THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
//
//////////////////////////////////////////////////////////////////////////////
#include "StructureWallBreakBehavior.h"
#include "GbmpBuiltInParameterDefinitions.h"
#include "IDocument.h"
#include "IElement.h"
#include "IElementParameters.h"
#include "IElementPosition.h"
#include "IParameter.h"
#include "IParameterDefinitionLibrary.h"
#include "IPositionTwoAssociatedPlanesWithFourOffsets.h"

#include "EnableCompileWarning_The_LAST_IncludeInCpp.h"
using namespace gcmp;

void gcmp::StructureGradientWallBreakBehavior::BreakConcreteElement(const IDocument * pDoc, IElement * pBreakElement, IElement * pNewElement, double breakPtToStartPtRatio, bool isBreakElementRetainOldElementId)
{
    DBG_WARN_AND_RETURN_VOID_UNLESS(pDoc != nullptr, L"pDoc为空",L"GDMPLab",L"2024-03-30");
    DBG_WARN_AND_RETURN_VOID_UNLESS(pBreakElement != nullptr, L"pBreakElement为空",L"GDMPLab",L"2024-03-30");
    DBG_WARN_AND_RETURN_VOID_UNLESS(pNewElement != nullptr, L"pNewElement为空",L"GDMPLab",L"2024-03-30");
    IElementPosition *breakPosBehavior = pBreakElement->GetElementPosition();
    IElementPosition *newPosBehavior = pNewElement->GetElementPosition();
    DBG_WARN_AND_RETURN_VOID_UNLESS(breakPosBehavior != nullptr, L"breakPosBehavior为空",L"GDMPLab",L"2024-03-30");
    DBG_WARN_AND_RETURN_VOID_UNLESS(newPosBehavior != nullptr, L"newPosBehavior为空",L"GDMPLab",L"2024-03-30");

    IPositionTwoAssociatedPlanesWithFourOffsets *pBreakTwoAssociatedPlane = dynamic_cast<IPositionTwoAssociatedPlanesWithFourOffsets *>(breakPosBehavior->GetPositionAssociatedPlane());
    IPositionTwoAssociatedPlanesWithFourOffsets *pNewTwoAssociatedPlane = dynamic_cast<IPositionTwoAssociatedPlanesWithFourOffsets *>(newPosBehavior->GetPositionAssociatedPlane());
    DBG_WARN_AND_RETURN_VOID_UNLESS(pBreakTwoAssociatedPlane != nullptr, L"pBreakTwoAssociatedPlane为空",L"GDMPLab",L"2024-03-30");
    DBG_WARN_AND_RETURN_VOID_UNLESS(pNewTwoAssociatedPlane != nullptr, L"pNewTwoAssociatedPlane为空",L"GDMPLab",L"2024-03-30");

    if (!pBreakElement->GetElementParameters())
        return;
    double startPointToBottomLevelOffset = pBreakElement->GetElementParameters()->GetParameterByUid(PARAMETER_UID(StartBottomAssociatedLevelOffsetBuiltInParameter))->GetValueAsDouble();  //起点底部标高偏移
    double endPointToBottomLevelOffset = pBreakElement->GetElementParameters()->GetParameterByUid(PARAMETER_UID(EndBottomAssociatedLevelOffsetBuiltInParameter))->GetValueAsDouble();   //终点底部标高偏移
    double startPointToTopLevelOffset = pBreakElement->GetElementParameters()->GetParameterByUid(PARAMETER_UID(StartTopAssociatedLevelOffsetBuiltInParameter))->GetValueAsDouble();  //起点顶部标高偏移
    double endPointToTopLevelOffset = pBreakElement->GetElementParameters()->GetParameterByUid(PARAMETER_UID(EndTopAssociatedLevelOffsetBuiltInParameter))->GetValueAsDouble();   //终点顶部标高偏移

                                                                                                                                                                                 //1、计算底部标高偏移
    double newBottomOffsetToLevel = CalculateNewBottomOffset(startPointToBottomLevelOffset, endPointToBottomLevelOffset, breakPtToStartPtRatio);
    if (isBreakElementRetainOldElementId)
    {
        pBreakTwoAssociatedPlane->SetEndPointToBottomAssociatedPlaneOffset(newBottomOffsetToLevel);
        pNewTwoAssociatedPlane->SetStartPointToBottomAssociatedPlaneOffset(newBottomOffsetToLevel);
    }
    else
    {
        pBreakTwoAssociatedPlane->SetStartPointToBottomAssociatedPlaneOffset(newBottomOffsetToLevel);
        pNewTwoAssociatedPlane->SetEndPointToBottomAssociatedPlaneOffset(newBottomOffsetToLevel);
    }


    if (pBreakTwoAssociatedPlane->GetTopAssociatedPlaneId().IsValid())
    {
        //2、计算顶部标高偏移
        double newTopOffsetToLevel = CalculateNewTopOffset(startPointToTopLevelOffset, endPointToTopLevelOffset, breakPtToStartPtRatio);
        if (isBreakElementRetainOldElementId)
        {
            pBreakTwoAssociatedPlane->SetEndPointToTopAssociatedPlaneOffset(newTopOffsetToLevel);
            pNewTwoAssociatedPlane->SetStartPointToTopAssociatedPlaneOffset(newTopOffsetToLevel);
        }
        else
        {
            pBreakTwoAssociatedPlane->SetStartPointToTopAssociatedPlaneOffset(newTopOffsetToLevel);
            pNewTwoAssociatedPlane->SetEndPointToTopAssociatedPlaneOffset(newTopOffsetToLevel);
        }

        //SetStartPointToTopLevelOffset等函数不会触发UpdatePositionParametersToFamilyParameters操作
        breakPosBehavior->UpdatePositionParametersToFamilyParameters();
        newPosBehavior->UpdatePositionParametersToFamilyParameters();
    }
    else  //无顶部关联时，需要计算出打断点处墙高
    {
        AdjustGradientWallHeight(pBreakElement, pNewElement, breakPtToStartPtRatio, isBreakElementRetainOldElementId);
    }
}

double gcmp::StructureGradientWallBreakBehavior::CalculateNewBottomOffset(double startPointToBottomLevelOffset, double endPointToBottomLevelOffset, double breakPtToStartPtRatio)
{
    if (fabs(startPointToBottomLevelOffset - endPointToBottomLevelOffset) <= Constants::DISTANCE_ITERATION_EPS)
    {
        return startPointToBottomLevelOffset;
    }

    //情形1：起点底部标高偏移 <= 终点底部标高偏移时 
    if (startPointToBottomLevelOffset < endPointToBottomLevelOffset)
    {
        double diffLevelOffset = endPointToBottomLevelOffset - startPointToBottomLevelOffset;  //偏移差值
        double distOfNewPositionLineToBottomLevel = endPointToBottomLevelOffset;  //新定位线到底标高平面距离
        double breakPointToNewPositionLine = (1 - breakPtToStartPtRatio)*diffLevelOffset;  //按比例计算出打断点到新定位线的距离

        return  -1 * breakPointToNewPositionLine + distOfNewPositionLineToBottomLevel;
    }


    //情形2：起点底部标高偏移 > 终点底部标高偏移时 
    double diffLevelOffset = startPointToBottomLevelOffset - endPointToBottomLevelOffset;
    double distOfNewPositionLineToBottomLevel = endPointToBottomLevelOffset;
    double breakPointToNewPositionLine = (1 - breakPtToStartPtRatio)*diffLevelOffset;
    return  breakPointToNewPositionLine + distOfNewPositionLineToBottomLevel;
}

double gcmp::StructureGradientWallBreakBehavior::CalculateNewTopOffset(double startPointToTopLevelOffset, double endPointToTopLevelOffset, double breakPtToStartPtRatio)
{
    if (fabs(startPointToTopLevelOffset - endPointToTopLevelOffset) <= Constants::DISTANCE_ITERATION_EPS)
    {
        return startPointToTopLevelOffset;
    }

    //情形1：起点顶部标高偏移 <= 终点顶部标高偏移时 
    if (startPointToTopLevelOffset < endPointToTopLevelOffset)
    {
        double diffLevelOffset = endPointToTopLevelOffset - startPointToTopLevelOffset;  //偏移差值
        double distOfNewPositionLineToTopLevel = startPointToTopLevelOffset;
        double breakPointToNewPositionLine = breakPtToStartPtRatio*diffLevelOffset;
        return  distOfNewPositionLineToTopLevel + breakPointToNewPositionLine;
    }

    //情形2：起点顶部标高偏移 > 终点顶部标高偏移时 
    double diffLevelOffset = startPointToTopLevelOffset - endPointToTopLevelOffset;  //偏移差值
    double distOfNewPositionLineToTopLevel = endPointToTopLevelOffset;
    double breakPointToNewPositionLine = (1 - breakPtToStartPtRatio)*diffLevelOffset;
    return  distOfNewPositionLineToTopLevel + breakPointToNewPositionLine;
}

void gcmp::StructureGradientWallBreakBehavior::AdjustGradientWallHeight(gcmp::IElement * pBreakElement, gcmp::IElement * pNewElement, double breakPtToStartPtRatio, bool isBreakElementRetainOldElementId)
{
    DBG_WARN_AND_RETURN_VOID_UNLESS(pBreakElement != nullptr, L"pBreakElement为空",L"GDMPLab",L"2024-03-30");
    DBG_WARN_AND_RETURN_VOID_UNLESS(pNewElement != nullptr, L"pNewElement为空",L"GDMPLab",L"2024-03-30");
    if(!pBreakElement->GetElementParameters())
        return;
    double startPointWallHeight = pBreakElement->GetElementParameters()->GetParameterByUid(PARAMETER_UID(StartHeightBuiltInParameter))->GetValueAsDouble();
    double endPointWallHeight = pBreakElement->GetElementParameters()->GetParameterByUid(PARAMETER_UID(EndHeightBuiltInParameter))->GetValueAsDouble();
    if (fabs(startPointWallHeight - endPointWallHeight) <= Constants::DISTANCE_ITERATION_EPS)
    {
        return;
    }

    double breakPointHeight = 0;
    if (startPointWallHeight < endPointWallHeight)
    {
        double diffHeight = breakPtToStartPtRatio * (endPointWallHeight - startPointWallHeight);
        breakPointHeight = diffHeight + startPointWallHeight;
    }
    else
    {
        double diffHeight = (1 - breakPtToStartPtRatio) * (startPointWallHeight - endPointWallHeight);
        breakPointHeight = diffHeight + endPointWallHeight;
    }
    if (!pNewElement->GetElementParameters() || !pBreakElement->GetElementParameters())
        return;
    if (isBreakElementRetainOldElementId)
    {
        OwnerPtr<IParameter> startWallHeightParam = pNewElement->GetElementParameters()->GetParameterByUid(PARAMETER_UID(StartHeightBuiltInParameter));
        OwnerPtr<IParameter> endWallHeightParam = pBreakElement->GetElementParameters()->GetParameterByUid(PARAMETER_UID(EndHeightBuiltInParameter));
        startWallHeightParam->SetValueAsDouble(breakPointHeight);
        endWallHeightParam->SetValueAsDouble(breakPointHeight);
        pNewElement->GetElementParameters()->SetParameter(startWallHeightParam.get());
        pBreakElement->GetElementParameters()->SetParameter(endWallHeightParam.get());
    }
    else
    {
        OwnerPtr<IParameter> startWallHeightParam = pBreakElement->GetElementParameters()->GetParameterByUid(PARAMETER_UID(StartHeightBuiltInParameter));
        OwnerPtr<IParameter> endWallHeightParam = pNewElement->GetElementParameters()->GetParameterByUid(PARAMETER_UID(EndHeightBuiltInParameter));
        startWallHeightParam->SetValueAsDouble(breakPointHeight);
        endWallHeightParam->SetValueAsDouble(breakPointHeight);
        pBreakElement->GetElementParameters()->SetParameter(startWallHeightParam.get());
        pNewElement->GetElementParameters()->SetParameter(endWallHeightParam.get());
    }
}
